/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/


/******************************************************************/
/* Includes                                                       */
/******************************************************************/

// module's interface
#include "FilterWnd.h"

// standard headers
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>

// project's headers
#include "Assert.h"
#include "DbgPrint.h"
#include "Condition.h"
#include "Filter.h"
#include "FilterSet.h"
#include "Language.h"
#include "ProgPathDlg.h"
#include "ProjectInfo.h"
#include "Resources.h"
#include "RuleDlg.h"
#include "SpyDll.h"
#include "SpyServer.h"


/******************************************************************/
/* Internal constants                                             */
/******************************************************************/

// column identifiers
#define COL_PROGRAM	0
#define COL_REASON	0
#define COL_PARAM1	1
#define COL_PARAM2	2
#define COL_REACTION	3
#define COL_VERBOSITY	4
#define COL_OPTIONS	5

// radio button height
#define CY_RADIOBTN		20

// window class name
#define WC_FILTERWND TEXT("FilterWnd")

// window messages
#define WM_UPDATEPROGRAMLIST	(WM_UPDATEFILTERS+1)
#define WM_UPDATERULELIST	(WM_UPDATEFILTERS+2)


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

LRESULT CALLBACK _FilterWnd_WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ;


/******************************************************************/
/* Exported function : RegisterClass                              */
/******************************************************************/

BOOL FilterWnd_RegisterClass (HINSTANCE hInstance) 
{
  WNDCLASS wndclass ;

  wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
  wndclass.lpfnWndProc   = _FilterWnd_WndProc ;
  wndclass.cbClsExtra    = 0 ;
  wndclass.cbWndExtra    = 0 ;
  wndclass.hInstance     = hInstance ;
  wndclass.hIcon         = LoadIcon (NULL, IDI_APPLICATION) ;
  wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  wndclass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1) ;
  wndclass.lpszMenuName  = NULL ;
  wndclass.lpszClassName = WC_FILTERWND ;

  return 0!=RegisterClass (&wndclass) ;
}


/******************************************************************/
/* Exported function : CreateWindow                               */
/******************************************************************/

HWND FilterWnd_CreateWindow (HINSTANCE hInstance, HWND hwndParent)
{  
  return CreateWindow (WC_FILTERWND, NULL,
		       WS_CHILD,
		       CW_USEDEFAULT, CW_USEDEFAULT, 
		       CW_USEDEFAULT, CW_USEDEFAULT, 
		       hwndParent, NULL, hInstance, NULL) ;
}


/******************************************************************/
/* Internal function : EnumRulesCallback                          */
/******************************************************************/

VOID _FilterWnd_EnumRulesCallback (LPVOID pContext, RULE * pRule) 
{
  LVITEM lvi = { 0 } ;
  HWND hwndProcesses = (HWND)pContext ;
  TCHAR szBuffer[1024] ;

  ASSERT (pRule!=NULL) ;
  ASSERT (pRule->pCondition!=NULL) ;
    
  Condition_GetReasonAsString (pRule->pCondition, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT|LVIF_PARAM ;
  lvi.iItem = ListView_GetItemCount (hwndProcesses) ;
  lvi.iSubItem = COL_REASON ;
  lvi.pszText = szBuffer ;
  lvi.lParam = (LPARAM)pRule ;
  ListView_InsertItem (hwndProcesses, &lvi) ;

  Condition_GetParamAsString (pRule->pCondition, 0, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT ;
  lvi.iSubItem = COL_PARAM1 ;
  lvi.pszText = szBuffer ;
  ListView_SetItem (hwndProcesses, &lvi) ;

  Condition_GetParamAsString (pRule->pCondition, 1, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT ;
  lvi.iSubItem = COL_PARAM2 ;
  lvi.pszText = szBuffer ;
  ListView_SetItem (hwndProcesses, &lvi) ;

  // reaction
  Rule_GetReactionString (pRule, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT ;
  lvi.iSubItem = COL_REACTION ;
  lvi.pszText = szBuffer ;
  ListView_SetItem (hwndProcesses, &lvi) ;
  
  // verbosity
  Rule_GetVerbosityString (pRule, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT ;
  lvi.iSubItem = COL_VERBOSITY ;
  lvi.pszText = szBuffer ;
  ListView_SetItem (hwndProcesses, &lvi) ;

  // options
  Rule_GetOptionsString (pRule, szBuffer, 64) ;
  lvi.mask = LVIF_TEXT ;
  lvi.iSubItem = COL_OPTIONS ;
  lvi.pszText = szBuffer ;
  ListView_SetItem (hwndProcesses, &lvi) ;
}


/******************************************************************/
/* Internal function : WndProc                                    */
/******************************************************************/

LRESULT CALLBACK _FilterWnd_WndProc (HWND hwnd, UINT message, 
				    WPARAM wParam, LPARAM lParam)
{  
  static HWND		g_hwndPrograms ;
  static HWND		g_hwndRules ;
  static HWND		g_hwndEnableHook ;
  static HWND		g_hwndDisableHook ;
  static HINSTANCE	g_hInstance ;
  static HMENU		g_hmenuPrograms ;
  static HMENU		g_hmenuRules ;
  static HFILTER	g_hfltCurrent ;

  TCHAR			szBuffer[1024] ;
  int			i, nWidth, nHeight ;
  POINT			point ;
  HFILTERSET		hFilterSet ;
  //HFILTER		hFilter ;
  LVITEM		lvi ;
  LVCOLUMN		lvc ;
  RULE			*pRule ;
  
  union {    
    NMHDR		*header ;
    NMITEMACTIVATE	*itemactivate ;
    NMLISTVIEW		*listview ; 
  } pnm ;

  switch (message)
    {
    case WM_CREATE:
      
      g_hfltCurrent = NULL ;
	
      g_hInstance = ((CREATESTRUCT*)lParam)->hInstance ;
            
      g_hwndPrograms = CreateWindowEx (WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
					WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT|LVS_SINGLESEL,
					0,0,0,0, hwnd, (HMENU)IDC_PROGRAMLIST, g_hInstance, NULL) ;
	
      g_hwndRules = CreateWindowEx (WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL,
				    WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT,
				    0,0,0,0, hwnd, (HMENU)IDC_RULELIST, g_hInstance, NULL) ;

      g_hwndEnableHook = CreateWindow (WC_BUTTON, NULL, WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON,
				       0,0,0,0, hwnd, (HMENU)IDC_ENABLE_HOOK, g_hInstance, NULL) ;

      SendMessage (g_hwndEnableHook, WM_SETFONT, 
		   (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0) ;
      
      g_hwndDisableHook = CreateWindow (WC_BUTTON, NULL, WS_CHILD|WS_VISIBLE|BS_AUTORADIOBUTTON,
					0,0,0,0, hwnd, (HMENU)IDC_DISABLE_HOOK, g_hInstance, NULL) ;
      
      SendMessage (g_hwndDisableHook, WM_SETFONT, 
		   (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0) ;


      // menus will be created by WM_LANGUAGECHANGED
      g_hmenuRules = NULL ;
      g_hmenuPrograms = NULL ;

      //
      // create columns in list view controls
      //

      ZeroMemory (&lvc, sizeof(lvc)) ;
      lvc.mask		= LVCF_TEXT|LVCF_WIDTH|LVCF_SUBITEM ;
      lvc.pszText	= TEXT("") ;

      lvc.cx		= 400 ;
      lvc.iSubItem	= COL_PROGRAM ;
      ListView_InsertColumn (g_hwndPrograms, lvc.iSubItem, &lvc) ; 
      
      lvc.cx		= 200 ;
      lvc.iSubItem	= COL_REASON   ;
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 
      
      lvc.cx		= 100 ;
      lvc.iSubItem	= COL_PARAM1 ;
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 

      lvc.iSubItem	= COL_PARAM2 ;
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 
      
      lvc.iSubItem	= COL_REACTION ;
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 

      lvc.iSubItem	= COL_VERBOSITY ;
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 

      lvc.iSubItem	= COL_OPTIONS ;      
      ListView_InsertColumn (g_hwndRules, lvc.iSubItem, &lvc) ; 
        
      g_hfltCurrent = FilterSet_GetFilterByNum (SpySrv_GetFilterSet(), 0) ;

      SendMessage (hwnd, WM_LANGUAGECHANGED, 0, 0) ;

      return 0 ; // case WM_CREATE:

    case WM_DESTROY: 

      // destroy menus
      DestroyMenu (g_hmenuPrograms) ;
      DestroyMenu (g_hmenuRules) ;

      return 0 ; // case WM_DESTROY:   
      
    case WM_SIZE:
      nWidth = LOWORD (lParam) ;
      nHeight = HIWORD (lParam) ;
      MoveWindow (g_hwndPrograms, 0, 0, nWidth, nHeight/3-CY_RADIOBTN, TRUE) ;
      MoveWindow (g_hwndEnableHook, 0, nHeight/3-CY_RADIOBTN, nWidth, CY_RADIOBTN, TRUE) ;
      MoveWindow (g_hwndDisableHook, 0, nHeight/3, nWidth, CY_RADIOBTN, TRUE) ;
      MoveWindow (g_hwndRules, 0, nHeight/3+CY_RADIOBTN, nWidth, 2*nHeight/3-CY_RADIOBTN, TRUE) ;
      return 0 ;

    case WM_LANGUAGECHANGED:
        
      // 
      // Set text of columns
      //
      ZeroMemory (&lvc, sizeof(lvc)) ;
      lvc.mask		= LVCF_TEXT ;      

      lvc.pszText	= (LPTSTR) STR_DEF(_PROGRAM,TEXT("Program")) ;
      ListView_SetColumn (g_hwndPrograms, COL_PROGRAM, &lvc) ; 

      lvc.pszText	= (LPTSTR) STR_DEF (_REASON,TEXT("Function")) ;
      ListView_SetColumn (g_hwndRules, COL_REASON, &lvc) ; 

      lvc.pszText	= szBuffer ;
      wsprintf (szBuffer, TEXT("%s 1"), STR_DEF(_PARAM,TEXT("Param"))) ; ;
      ListView_SetColumn (g_hwndRules, COL_PARAM1, &lvc) ;     

      lvc.pszText	= szBuffer ;
      wsprintf (szBuffer, TEXT("%s 2"), STR_DEF(_PARAM,TEXT("Param"))) ; ;
      ListView_SetColumn (g_hwndRules, COL_PARAM2, &lvc) ;  

      lvc.pszText	= (LPTSTR) STR_DEF (_REACTION, TEXT("Reaction")) ;
      ListView_SetColumn (g_hwndRules, COL_REACTION, &lvc) ;

      lvc.pszText	= (LPTSTR) STR_DEF (_VERBOSITY,TEXT("Verbosity")) ;
      ListView_SetColumn (g_hwndRules, COL_VERBOSITY, &lvc) ;
      
      lvc.pszText	= (LPTSTR) STR_DEF (_OPTIONS,TEXT("Options")) ;
      ListView_SetColumn (g_hwndRules, COL_OPTIONS, &lvc) ;

      // create process menu
      DestroyMenu (g_hmenuPrograms) ;
      g_hmenuPrograms = CreatePopupMenu () ;
      AppendMenu (g_hmenuPrograms, MF_STRING|MF_GRAYED, 0, 
		  STR_DEF(_PROGRAM_MENU,TEXT("Program menu"))) ;
      AppendMenu (g_hmenuPrograms, MF_SEPARATOR, 0, NULL) ;
      AppendMenu (g_hmenuPrograms, MF_STRING, IDM_PROGRAM_ADD, 
		  STR_DEF(_ADD_PROGRAM,TEXT("Add program"))) ;
      AppendMenu (g_hmenuPrograms, MF_STRING, IDM_PROGRAM_EDIT, 
		  STR_DEF(_EDIT_PROGRAM,TEXT("Edit program"))) ;
      AppendMenu (g_hmenuPrograms, MF_STRING, IDM_PROGRAM_REMOVE,
		  STR_DEF(_REMOVE_PROGRAM,TEXT("Remove program"))) ;
    
      // create rule menu
      DestroyMenu (g_hmenuRules) ;
      g_hmenuRules = CreatePopupMenu () ;
      AppendMenu (g_hmenuRules, MF_STRING|MF_GRAYED, 0, TEXT ("Filter menu")) ;
      AppendMenu (g_hmenuRules, MF_SEPARATOR, 0, NULL) ;
      AppendMenu (g_hmenuRules, MF_STRING, IDM_RULE_ADD, TEXT ("Add rule")) ;
      AppendMenu (g_hmenuRules, MF_STRING, IDM_RULE_EDIT, TEXT ("Edit rule")) ;
      AppendMenu (g_hmenuRules, MF_STRING, IDM_RULE_REMOVE, TEXT ("Remove rule")) ;
      AppendMenu (g_hmenuRules, MF_SEPARATOR, 0, NULL) ;
      AppendMenu (g_hmenuRules, MF_STRING, IDM_RULE_UP, TEXT ("Move Up")) ;
      AppendMenu (g_hmenuRules, MF_STRING, IDM_RULE_DOWN, TEXT ("Move Down")) ;

      // check boxes
      SetWindowText (g_hwndEnableHook, STR_DEF(_HOOK_THIS_PROGRAM,
					       TEXT("Hook this program"))) ;
      SetWindowText (g_hwndDisableHook, STR_DEF(_DONT_HOOK_THIS_PROGRAM,
						TEXT("Don't hook this program"))) ;
      
      PostMessage (hwnd, WM_UPDATEFILTERS, 0, 0) ;
      PostMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;

      return 0 ;

    case WM_UPDATEFILTERS :
      
    case WM_UPDATEPROGRAMLIST:
      {
	UINT i ;
	HFILTERSET hFilterSet ;
	LV_ITEM lvi ;
	
	ListView_DeleteAllItems (g_hwndPrograms) ;

	hFilterSet = SpySrv_GetFilterSet () ;
	
	for( i=0 ; i<FilterSet_GetFilterCount(hFilterSet) ; i++ )
	  {
	    HFILTER hFilter = FilterSet_GetFilterByNum(hFilterSet,i) ;
	    ZeroMemory (&lvi, sizeof(lvi)) ;
	    lvi.mask = LVIF_TEXT|LVIF_STATE ;
	    lvi.iItem = i ;
	    lvi.state = hFilter==g_hfltCurrent ? LVIS_SELECTED|LVIS_FOCUSED : 0 ;
	    lvi.stateMask = LVIS_SELECTED|LVIS_FOCUSED ;
	    lvi.pszText = (TCHAR*)Filter_GetProgram(hFilter) ;
	    ListView_InsertItem (g_hwndPrograms, &lvi) ;
	  }
      }
      return 0 ;      

    case WM_UPDATERULELIST:
      
      ListView_DeleteAllItems (g_hwndRules) ;

      EnableWindow (g_hwndEnableHook, g_hfltCurrent!=NULL) ;
      EnableWindow (g_hwndDisableHook, g_hfltCurrent!=NULL) ;

      if( g_hfltCurrent ) 
	{
	  Filter_EnumRules (g_hfltCurrent, _FilterWnd_EnumRulesCallback, g_hwndRules) ;
	  
	  SendMessage (g_hwndEnableHook, BM_SETCHECK, 
		       Filter_IsHookEnabled(g_hfltCurrent) ? BST_CHECKED : BST_UNCHECKED, 0) ;
	  SendMessage (g_hwndDisableHook, BM_SETCHECK, 
		       Filter_IsHookEnabled(g_hfltCurrent) ? BST_UNCHECKED : BST_CHECKED, 0) ;
	}

      return 0 ;

    case WM_COMMAND:

      switch( LOWORD(wParam))
	{
	case IDM_PROGRAM_ADD:

	  szBuffer[0] = 0 ;

	  if( IDOK==DialogBoxParam(g_hInstance, MAKEINTRESOURCE(DLG_PROGPATH), 
				   hwnd, ProgPathDlg_DlgProc, (LPARAM)szBuffer) )
	    {
	      hFilterSet = SpySrv_GetFilterSet () ;
	      g_hfltCurrent = Filter_Create (szBuffer) ;
	      FilterSet_AddFilter (hFilterSet, g_hfltCurrent) ;
	      SendMessage (hwnd, WM_UPDATEPROGRAMLIST, 0, 0) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;
	    }

	  return 0 ; // case IDM_PROGRAM_ADD:

	case IDM_PROGRAM_EDIT:

	  if( ! g_hfltCurrent ) return 0 ;

	  szBuffer[0] = 0 ;

	  _tcscpy (szBuffer, Filter_GetProgram (g_hfltCurrent)) ;

	  if( IDOK==DialogBoxParam(g_hInstance, MAKEINTRESOURCE(DLG_PROGPATH), 
				   hwnd, ProgPathDlg_DlgProc, (LPARAM)szBuffer) )
	    {
	      Filter_SetProgram (g_hfltCurrent, szBuffer) ;
	      SendMessage (hwnd, WM_UPDATEPROGRAMLIST, 0, 0) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;
	    }

	  return 0 ; // case IDM_PROGRAM_EDIT:

	case IDM_PROGRAM_REMOVE:

	  if( ! g_hfltCurrent ) return 0 ;

	  wsprintf (szBuffer,
		    STR_DEF(_SURE_REMOVE_FILTER_FOR_S,
			    TEXT("Are you sure you want to remove filter for \"%s\" ?")),
		    Filter_GetProgram(g_hfltCurrent)) ;
	      
	  if( IDYES==MessageBox(hwnd,szBuffer,TEXT(APPLICATION_NAME),
				MB_ICONQUESTION|MB_YESNO) )
	    {
	      hFilterSet = SpySrv_GetFilterSet () ;

	      FilterSet_Remove (hFilterSet, g_hfltCurrent) ;
	      
	      Filter_Destroy (g_hfltCurrent) ;
	      g_hfltCurrent = NULL ;
	      
	      SendMessage (hwnd, WM_UPDATEPROGRAMLIST, 0, 0) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;
	    }

	  return 0 ; // case IDM_PROGRAM_REMOVE:

	case IDM_RULE_ADD:

	  if( ! g_hfltCurrent )
	    return 0 ;
	  	  
	  pRule = (RULE*) calloc (1, sizeof(RULE)) ;

	  if( IDOK==DialogBoxParam(g_hInstance, MAKEINTRESOURCE(DLG_RULE), 
				   hwnd, RuleDlg_DlgProc, (LPARAM)pRule) )
	    {
	       Filter_AddRule (g_hfltCurrent, pRule) ;
	       SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;	
	    }
	  else
	    {
	      free (pRule) ;
	    }
	  
	  return 0 ; // case IDM_RULE_ADD :

	case IDM_RULE_EDIT:
	  
	  if( ! g_hfltCurrent ) return 0 ;

	  i = -1 ;
	  while( 1 )
	    {
	      i = ListView_GetNextItem (g_hwndRules, i, LVNI_ALL|LVNI_SELECTED) ;
	      if( i<0 ) break ;
	      
	      ZeroMemory (&lvi, sizeof(lvi)) ;
	      lvi.mask		= LVIF_PARAM ;
	      lvi.iItem		= i ;
	      lvi.iSubItem	= COL_REASON ;
	      ListView_GetItem (g_hwndRules, &lvi) ;

	      ASSERT (lvi.lParam!=0) ;	     

	      if( IDOK==DialogBoxParam(g_hInstance, MAKEINTRESOURCE(DLG_RULE), 
				       hwnd, RuleDlg_DlgProc, lvi.lParam) )
		SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;		 

	    } // while( 1 )	
	  
	  return 0 ; // case IDM_RULE_EDIT:
      
	case IDM_RULE_REMOVE:

	  if( ! g_hfltCurrent ) return 0 ;
	  
	  //i = -1 ;   TO BE TESTED      -------------v
	  while( 1 )
	    {
	      i = ListView_GetNextItem (g_hwndRules, -1, LVNI_ALL|LVNI_SELECTED) ;
	      if( i<0 ) break ;
	      
	      ZeroMemory (&lvi, sizeof(lvi)) ;
	      lvi.mask		= LVIF_PARAM ;
	      lvi.iItem		= i ;
	      lvi.iSubItem	= COL_REASON ;
	      ListView_GetItem (g_hwndRules, &lvi) ;

	      ASSERT (lvi.lParam!=0) ;	     

	      Filter_DeleteRule (g_hfltCurrent, (RULE*)lvi.lParam) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;		 

	    } // while( 1 )	
	  
	  return 0 ; // case IDM_RULE_REMOVE:

	case IDM_RULE_UP:

	  if( ! g_hfltCurrent ) return 0 ;
	  
	  i = -1 ;
	  while( 1 )
	    {
	      i = ListView_GetNextItem (g_hwndRules, i, LVNI_ALL|LVNI_SELECTED) ;
	      if( i<0 ) break ;

	      ZeroMemory (&lvi, sizeof(lvi)) ;
	      lvi.mask		= LVIF_PARAM ;
	      lvi.iItem		= i ;
	      lvi.iSubItem	= COL_REASON ;
	      ListView_GetItem (g_hwndRules, &lvi) ;

	      ASSERT (lvi.lParam!=0) ;	     

	      Filter_MoveRuleUp (g_hfltCurrent, (RULE*)lvi.lParam) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;		 

	    } // while( 1 )	
	  
	  return 0 ; // case IDM_RULE_UP:

	case IDM_RULE_DOWN:

	  if( ! g_hfltCurrent ) return 0 ;
	  
	  i = -1 ;
	  while( 1 )
	    {
	      i = ListView_GetNextItem (g_hwndRules, i, LVNI_ALL|LVNI_SELECTED) ;
	      if( i<0 ) break ;

	      ZeroMemory (&lvi, sizeof(lvi)) ;
	      lvi.mask		= LVIF_PARAM ;
	      lvi.iItem		= i ;
	      lvi.iSubItem	= COL_REASON ;
	      ListView_GetItem (g_hwndRules, &lvi) ;

	      ASSERT (lvi.lParam!=0) ;	     

	      Filter_MoveRuleDown (g_hfltCurrent, (RULE*)lvi.lParam) ;
	      SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;		 

	    } // while( 1 )	
	  
	  return 0 ; // case IDM_RULE_DOWN:

	case IDC_ENABLE_HOOK:
	  
	  if( ! g_hfltCurrent ) return 0 ;

	  if( HIWORD(wParam)==BN_CLICKED )
	    Filter_EnableHook (g_hfltCurrent, TRUE) ;

	  return 0 ; // case IDC_ENABLE_HOOK:

	case IDC_DISABLE_HOOK:

	  if( ! g_hfltCurrent ) return 0 ;

	  if( HIWORD(wParam)==BN_CLICKED )
	    Filter_EnableHook (g_hfltCurrent, FALSE) ;

	  return 0 ; // case IDC_DISABLE_HOOK:

	} // switch( LOWORD(wParam) )
      
      break ; // case WM_COMMAND:
  
    case WM_NOTIFY:
      
      pnm.header = (NMHDR*)lParam ;
      
      switch( pnm.header->idFrom )
	{
	case IDC_PROGRAMLIST:
	  
	  switch( pnm.header->code )
	    {
	    case LVN_ITEMCHANGED:
	      
	      if( pnm.listview->uNewState & LVIS_SELECTED )
		{
		  hFilterSet = SpySrv_GetFilterSet () ;

		  g_hfltCurrent = FilterSet_GetFilterByNum (hFilterSet, pnm.listview->iItem) ;
		  
		  SendMessage (hwnd, WM_UPDATERULELIST, 0, 0) ;
		}	
	      
	      return 0 ; // case LVN_ITEMCHANGED:

	    case LVN_ITEMACTIVATE:
	      
	      // simulate a click on the menu
	      if( pnm.listview->iItem>0 )
		SendMessage (hwnd, WM_COMMAND, IDM_PROGRAM_EDIT, 0) ;
	      
	      return 0 ; // case NM_DBLCLICK:
	      
	    case NM_RCLICK:
	      
	      GetCursorPos (&point) ;
	      
	      if( pnm.listview->iItem>0 && ListView_GetSelectedCount(g_hwndPrograms)>0 ) {
		EnableMenuItem (g_hmenuPrograms, IDM_PROGRAM_EDIT, MF_BYCOMMAND|MF_ENABLED) ;
		EnableMenuItem (g_hmenuPrograms, IDM_PROGRAM_REMOVE, MF_BYCOMMAND|MF_ENABLED) ;
	      } 
	      else {
		EnableMenuItem (g_hmenuPrograms, IDM_PROGRAM_EDIT, MF_BYCOMMAND|MF_GRAYED) ;
		EnableMenuItem (g_hmenuPrograms, IDM_PROGRAM_REMOVE, MF_BYCOMMAND|MF_GRAYED) ;	  
	      } 
	      
	      TrackPopupMenu (g_hmenuPrograms, TPM_RIGHTBUTTON, point.x, point.y, 
			      0, hwnd, NULL) ;
	      
	      return 0 ;
	      
	    } //  switch( pnmh->code )
	  
	  break ; // case IDC_PROGRAMLIST:
	  
	case IDC_RULELIST:
	  
	  switch( pnm.header->code )
	    {
	    case LVN_ITEMACTIVATE:

	      // simulate a click on the menu
	      SendMessage (hwnd, WM_COMMAND, IDM_RULE_EDIT, 0) ;

	      return 0 ; // case LVN_ITEMACTIVATE:


	    case NM_RCLICK:
	      
	      if( ! g_hfltCurrent ) return 0 ;
	      
	      GetCursorPos (&point) ;
	      
	      if( ListView_GetSelectedCount(g_hwndRules)>0 ) {
		EnableMenuItem (g_hmenuRules, IDM_RULE_EDIT, MF_BYCOMMAND|MF_ENABLED) ;
		EnableMenuItem (g_hmenuRules, IDM_RULE_REMOVE, MF_BYCOMMAND|MF_ENABLED) ;		  
		} 
	      else {
		EnableMenuItem (g_hmenuRules, IDM_RULE_EDIT, MF_BYCOMMAND|MF_GRAYED) ;
		EnableMenuItem (g_hmenuRules, IDM_RULE_REMOVE, MF_BYCOMMAND|MF_GRAYED) ;		  
	      } 
	      
	      TrackPopupMenu (g_hmenuRules, TPM_RIGHTBUTTON, 
			      point.x, point.y, 0, hwnd, NULL) ;
	      
	      return 0 ; // case NM_RCLICK:
	    }
	  
	  break ; // case IDC_RULELIST:	    
	  
	}
      return 0 ;
    }

  return DefWindowProc (hwnd, message, wParam, lParam) ;
}
